(defclass card () ((value :initarg :value  :accessor value) 
		   (kind  :initarg :kind :accessor kind)))

(defun make-card (card)
  (make-instance 'card :kind (subseq card 1 2) :value (get-card-value (subseq card 0 1))))

(defun make-hand(hand)
  (mapcar #'make-card hand))

(defun get-card-value (value)
  (cond ((equal value "T") 10)
	((equal value "J") 11)
	((equal value "Q") 12)
	((equal value "K") 13)
	((equal value "A") 14)
	(T (parse-integer value))))

(defun flush(hand)
  (loop
     with kinds = (mapcar #'kind hand)
     for i in (cdr kinds)
     when (not (equal (car kinds) i)) do (return 0) 
     finally (return 1)))

(defun straight(hand)
  (loop with values = (sort (mapcar #'value hand) #'<) 
     for i in values 
     for j in (cdr values)
     when (/= (1+ i) j) do (return 0)
     finally (return j)))

(defun straight-flush(hand)
  (* (straight hand) (flush hand)))

(defun royal-flush(hand)
  (* (straight-flush hand) (if (= 10 (loop for i in hand minimize (value i))) 1 0)))

(defun count-in-list(l)
  (loop for i in (cdr l)
     with no = ()
     with last = (car l)
     with c = 1 
     when (= i last) do (incf c)
     when (/= i last) do (and (setf no (cons (list last c) no)) (setf c 1) (setf last i) )
     finally (return (cons (list last c) no))))

(defun count-value(hand)
  (count-in-list (sort (mapcar #'value hand) #'<)))

(defun has-no(hand no)
  (loop for i in (count-value hand) 
     when (= (cadr i) no) do (return (car i))
     finally (return 0)))

(defun four(hand)
  (has-no hand 4))

(defun three(hand)
  (has-no hand 3))

(defun pair(hand)
  (has-no hand 2))

(defun high(hand)
  (loop for i in (mapcar #'value hand) maximize i))

(defun twopairs(hand)
  (loop for i in (count-value hand) 
     with pair = nil
     when (= (cadr i) 2) do (if pair (return (+ pair (car i))) (setf pair (car i)))
     finally (return 0)))

(defun full(hand)
  (let ((sorted (sort (count-value hand) #'(lambda (a b) (> (cadr a) (cadr b))))))
    (if (and (= 3 (cadr (car sorted))) (= 2 (cadr (cadr sorted)))) (car (car sorted)) 0 )))

(defun bigger-compare-with-func(func a b)
  (> (funcall func a) (funcall func b)))

(defun bigger-high-compare(a b)
  (loop for i in (sort (mapcar #'value a) #'>) 
     for j in  (sort (mapcar #'value b) #'>)
     when (> i j) do (return T)
     finally (return nil)))

(defun bigger(a b)
  (loop for i in (list #'royal-flush #'straight-flush #'four #'full #'flush #'straight #'three #'twopairs #'pair #'high)
     when (bigger-compare-with-func i a b) do (return t) 
     when (bigger-compare-with-func i b a) do (return nil)
     finally (return (bigger-high-compare a b))))

(defun read-hands()
  (mapcar #'make-hand (with-open-file (stream "c:/---/workspace/euler/poker.txt")
    (read stream))))

;(list (make-card "TH") (make-card "JH") (make-card "AH") (make-card "9H") (make-card "KH"))
(defun probl054()
  (loop with hands = (read-hands) for i from 1 to 1000
       for c = hands then (cddr c) count (bigger (car c) (cadr c))))
